package edu.hitsz.application.Game;

import edu.hitsz.aircraft.*;
import edu.hitsz.aircraft.enemy.AbstractEnemy;
import edu.hitsz.aircraft.enemy.BossEnemy;
import edu.hitsz.application.*;
import edu.hitsz.bullet.BaseBullet;
import edu.hitsz.basic.AbstractFlyingObject;
import edu.hitsz.factory.enemyFactory.*;
import edu.hitsz.observer.BombObserver;
import edu.hitsz.prop.AbstractProp;
import edu.hitsz.prop.BombProp;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;

/**
 * 游戏主面板，游戏启动
 *
 * @author hitsz
 */
public abstract class Game extends JPanel {

    private int backGroundTop = 0;

    /**
     * Scheduled 线程池，用于任务调度
     */
    private final ScheduledExecutorService executorService;

    /**
     * 时间间隔(ms)，控制刷新频率
     */
    private int timeInterval = 40;


    private final HeroAircraft heroAircraft;
    private final List<AbstractAircraft> enemyAircrafts;

    private final List<BaseBullet> heroBullets;
    private final List<BaseBullet> enemyBullets;

    private final List<AbstractProp> propList;

    /**
     * 屏幕中出现的敌机最大数量
     */
    protected int enemyMaxNumber;

    /**
     * 当前得分
     */
    private int score = 0;
    private int scoreBossCycle=0;
    /**
     * 当前时刻
     */
    private int time = 0;

    /**
     * 周期（ms)
     * 指示子弹的发射、敌机的产生频率
     */
    private int cycleDuration = 600;
    private int cycleTime = 0;

    /**
     * 游戏结束标志
     */
    private boolean gameOverFlag = false;

    /**
     * 通用BackGround
     */
    protected BufferedImage background_image;

    /**
     * 产生精英机概率
     */
    protected double elite_Probability;

    /**
     * Boss敌机是否已创建
     */
    public static boolean BOSS_CREATED=false;

    /**
     * 每隔若干周期刷新超级精英机
     * 通过修改该周期数改变超级精英机刷新速度
     */
    protected int elitePlus_cycles;
    private int elitePlus_cycle_cnt;

    /**
     * 设置游戏随时间难度级别
     */
    private int gameLevel_cycle_cnt;
    public static int gameLevel=0;

    public abstract boolean needLevelUp();

    /**
     * 设置游戏难度
     */
    public abstract void setGameMode();

    public Game() {
        //单例创造英雄机
        heroAircraft =HeroAircraft.getInstance();

        //创建敌机对象列表
        enemyAircrafts = new LinkedList<>();

        //创建英雄子弹列表，敌机子弹列表
        heroBullets = new LinkedList<>();
        enemyBullets = new LinkedList<>();

        //创建道具列表
        propList=new LinkedList<>();

        /**
         * Scheduled 线程池，用于定时任务调度
         * 关于alibaba code guide：可命名的 ThreadFactory 一般需要第三方包
         * apache 第三方库： org.apache.commons.lang3.concurrent.BasicThreadFactory
         */
        //参数：核心线程大小，线程工厂
        this.executorService = new ScheduledThreadPoolExecutor(20,
                new BasicThreadFactory.Builder().namingPattern("game-action-%d").daemon(true).build());

        //启动英雄机鼠标监听
        new HeroController(this, heroAircraft);

    }

    /**
     * 游戏启动入口，执行游戏逻辑
     */

    public void action() {

        setGameMode();

        elitePlus_cycle_cnt=0;
        gameLevel_cycle_cnt=0;
        gameLevel=1;

        // 定时任务：绘制、对象产生、碰撞判定、击毁及结束判定
        Runnable task = () -> {
            time += timeInterval;

            // 周期性执行（控制频率）
            if (timeCountAndNewCycleJudge()) {  //周期判断条件
//                System.out.println(time);
                elitePlus_cycle_cnt+=1;

                //设置每20个时间周期难度级别升级
                if(needLevelUp()) {
                    gameLevel_cycle_cnt += 1;
                    if (gameLevel_cycle_cnt >= 20) {
                        gameLevel_cycle_cnt = 0;
                        gameLevel += 1;
                        setLevelValue();
                    }
                }

                if(needBoss()) {
                    EnemyFactory enemyFactory = createBossEnemyFactory();
                    if(enemyFactory!=null) {
                        AbstractEnemy enemy = enemyFactory.createEnemy();
                        enemyAircrafts.add(enemy);
                    }
                }
                // 新敌机产生
                if (enemyAircrafts.size() < enemyMaxNumber) {
                    EnemyFactory enemyFactory;      //抽象敌机工厂
                    double rand = Math.random();    //生成随机数随机产生Mob,Elite
                    if (rand < 1-elite_Probability) {
                        //1.创建工厂对象
                        enemyFactory = new MobEnemyFactory();
                    } else {
                        enemyFactory = new EliteEnemyFactory();
                    }
                    //通过设置周期产生ElitePlus
                    if (elitePlus_cycle_cnt>=elitePlus_cycles) {
//                            System.out.println(elitePlus_cycle_cnt);
                        elitePlus_cycle_cnt=0;
                        enemyFactory = new ElitePlusEnemyFactory();
                    }

                    //2.调用对象的createObject方法
                    AbstractEnemy enemy=enemyFactory.createEnemy();
                    enemyAircrafts.add(enemy);
                }

                // 每个周期内，飞机射出子弹
                shootAction();
            }

            // 子弹移动
            bulletsMoveAction();

            // 飞机移动
            aircraftsMoveAction();

            //道具移动
            propsMoveAction();

            // 撞击检测
            crashCheckAction();

            // 后处理
            postProcessAction();

            //每个时刻重绘界面
            repaint();

            // 游戏结束
            // 检查英雄机是否存活
            if (heroAircraft.getHp() <= 0) {
                this.endGame();
            }

        };

        /**
         * 以固定延迟时间进行执行
         * 本次任务执行完成后，需要延迟设定的延迟时间，才会执行新的任务
         */
        executorService.scheduleWithFixedDelay(task, timeInterval, timeInterval, TimeUnit.MILLISECONDS);

        //添加背景音乐
        Thread bgm=new MusicThread(VideoManager.BGM);
        executorService.scheduleWithFixedDelay(bgm, 0, 2, TimeUnit.SECONDS);//间隔2s循环播放

        //添加boss出场音乐
        Thread bossBgm=new BossMusicThread(VideoManager.BGM_BOSS);
        executorService.scheduleWithFixedDelay(bossBgm, 0, 1, TimeUnit.SECONDS);//间隔2s循环播放

    }

    /**
     * 根据游戏级别，提升敌机属性
     */
    public void setLevelValue(){
        double rate = (gameLevel - 1) / 10.0 + 1;//提升倍率
        elite_Probability= Math.min((elite_Probability* rate),1);
        System.out.println();
        System.out.println("提高难度！当前级别："+gameLevel+"级。"+"敌机属性提升倍率："+rate+"。精英机产生概率："+elite_Probability);
    }


    /**
     * 游戏结束
     * 存储数据并跳转界面
     */
    public void endGame(){
        System.out.println("Game Over!");
        try {
            Thread gameOverBgm=new MusicThread(VideoManager.GAME_OVER);
            executorService.execute(gameOverBgm);
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        executorService.shutdown();
        Main.musicState=Main.MUSIC_OFF;
        gameOverFlag = true;

        //跳转页面
        RankScreen rankScreen=new RankScreen();
        Main.cardPanel.add(rankScreen.getMainPanel());
        Main.cardLayout.next(Main.cardPanel);
        //弹窗
        String name = JOptionPane.showInputDialog(null, "游戏结束，你的得分为"+score+"。\n"+"请输入名字记录得分",
                                                "我的标题", JOptionPane.QUESTION_MESSAGE);
        rankScreen.add(name,score);
    }


    /**
     *  钩子方法
     *  是否需要Boss敌机，是否每次召唤提升血量
     */
    public abstract boolean needBoss();
    public abstract boolean needUpBossHp();


    /**
     * 创建boss敌机
     * @return
     */
    //boss机创建次数
    public static int bossCreatedTime=0;

    //Boss机血量是否提升
    public static boolean upBossHp=false;

    protected int createBoss_scoreCycle;

    public EnemyFactory createBossEnemyFactory(){
        BOSS_CREATED=bossIsCreated();
        if(!BOSS_CREATED && scoreBossCycle>=createBoss_scoreCycle){
            bossCreatedTime+=1;
            scoreBossCycle=0;
            System.out.print("boss创建第"+bossCreatedTime+"次,");
            upBossHp=needUpBossHp();
            return new BossEnemyFactory();
        }else{
            return null;
        }
    }

    public boolean bossIsCreated(){
        for(AbstractAircraft enemy:enemyAircrafts){
           if(enemy instanceof BossEnemy){
               return true;
           }
        }
        return false;
    }

    //***********************
    //      Action 各部分
    //***********************
    private boolean timeCountAndNewCycleJudge() {
        cycleTime += timeInterval;
        if (cycleTime >= cycleDuration && cycleTime - timeInterval < cycleTime) {
            // 跨越到新的周期
            cycleTime %= cycleDuration;
            return true;
        } else {
            return false;
        }
    }

    private void shootAction() {
        // TODO 敌机射击
        for(AbstractAircraft enemy :enemyAircrafts){
            enemyBullets.addAll(enemy.shoot());
        }
        // 英雄射击
        heroBullets.addAll(heroAircraft.shoot());
    }

    private void bulletsMoveAction() {
        for (BaseBullet bullet : heroBullets) {
            bullet.forward();
        }
        for (BaseBullet bullet : enemyBullets) {
            bullet.forward();
        }
    }

    private void aircraftsMoveAction() {
        for (AbstractAircraft enemyAircraft : enemyAircrafts) {
            enemyAircraft.forward();
        }
    }

    private void propsMoveAction() {
        for (AbstractFlyingObject prop : propList) {
            prop.forward();
        }
    }


    /**
     * 碰撞检测：
     * 1. 敌机攻击英雄
     * 2. 英雄攻击/撞击敌机
     * 3. 英雄获得补给
     */
    private void crashCheckAction() {
        // TODO 敌机子弹攻击英雄
        for (BaseBullet enemyBullet:enemyBullets){
            if(enemyBullet.notValid()){
                continue;
            }
            if(heroAircraft.crash(enemyBullet)){
                // 英雄机与敌机子弹碰撞，损失一定生命值
                heroAircraft.decreaseHp(enemyBullet.getPower());
                enemyBullet.vanish();
            }
        }

        // 英雄子弹攻击敌机
        for (BaseBullet bullet : heroBullets) {
            if (bullet.notValid()) {
                continue;
            }
            for (AbstractAircraft enemyAircraft : enemyAircrafts) {
                if (enemyAircraft.notValid()) {
                    // 已被其他子弹击毁的敌机，不再检测
                    // 避免多个子弹重复击毁同一敌机的判定
                    continue;
                }
                if (enemyAircraft.crash(bullet)) {
                    // 敌机撞击到英雄机子弹，损失一定生命值
                    enemyAircraft.decreaseHp(bullet.getPower());
                    bullet.vanish();
                    Thread bulletHitBgm=new MusicThread(VideoManager.BULLET_HIT);
                    executorService.execute(bulletHitBgm);

                    if (enemyAircraft.notValid()) {
                        // TODO 获得分数，产生道具补给
                        score += 10;
                        scoreBossCycle+=10;
                        List<AbstractProp> prop = ((AbstractEnemy) enemyAircraft).dropProp(
                                enemyAircraft.getLocationX(), enemyAircraft.getLocationY()
                        );
                        propList.addAll(prop);
                    }

                }
                // 英雄机 与 敌机 相撞，均损毁
                if (enemyAircraft.crash(heroAircraft) || heroAircraft.crash(enemyAircraft)) {
                    enemyAircraft.vanish();
                    heroAircraft.decreaseHp(Integer.MAX_VALUE);
                }
            }
        }

        // Todo: 我方获得道具，道具生效
        for(AbstractProp abstractProp :propList){
            if(abstractProp.notValid()){
                continue;
            }
            //英雄机与道具碰撞
            if(heroAircraft.crash(abstractProp)|| abstractProp.crash(heroAircraft)){
                abstractProp.vanish();
                Thread getSupplyBgm=new MusicThread(VideoManager.GET_SUPPLY);
                executorService.execute(getSupplyBgm);

                //炸弹效果
                if(abstractProp instanceof BombProp){
                    Thread BombExplosionBgm=new MusicThread(VideoManager.BOMB_EXPLOSION);
                    executorService.execute(BombExplosionBgm);

                    //观察者模式实现爆炸效果
                    BombProp bombProp = (BombProp) abstractProp;
                    //加入观察者
                    for(BaseBullet bullet: enemyBullets){
                        bombProp.registerObserver((BombObserver) bullet);
                    }
                    for (AbstractAircraft enemy:enemyAircrafts){
                        bombProp.registerObserver((BombObserver) enemy);
                        if(!(enemy instanceof BossEnemy)){
                            score+=10;
                            scoreBossCycle+=10;
                        }
                    }

                }

                Runnable r=()->{
                    abstractProp.propAction();
                };
                executorService.execute(r);

            }
        }
    }

    /**
     * 后处理：
     * 1. 删除无效的子弹
     * 2. 删除无效的敌机
     * <p>
     * 无效的原因可能是撞击或者飞出边界
     */
    private void postProcessAction() {
        enemyBullets.removeIf(AbstractFlyingObject::notValid);
        heroBullets.removeIf(AbstractFlyingObject::notValid);
        enemyAircrafts.removeIf(AbstractFlyingObject::notValid);
        propList.removeIf(AbstractFlyingObject::notValid);
    }


    //***********************
    //      Paint 各部分
    //***********************

    /**
     * 重写paint方法
     * 通过重复调用paint方法，实现游戏动画
     *
     * @param  g
     */
    @Override
    public void paint(Graphics g) {
        super.paint(g);

        // 绘制背景,图片滚动
        g.drawImage(background_image, 0, this.backGroundTop - Main.WINDOW_HEIGHT, null);
        g.drawImage(background_image, 0, this.backGroundTop, null);


        this.backGroundTop += 1;
        if (this.backGroundTop == Main.WINDOW_HEIGHT) {
            this.backGroundTop = 0;
        }

        // 先绘制子弹，后绘制飞机
        // 这样子弹显示在飞机的下层
        paintImageWithPositionRevised(g, enemyBullets);
        paintImageWithPositionRevised(g, heroBullets);
        paintImageWithPositionRevised(g, enemyAircrafts);

        g.drawImage(ImageManager.HERO_IMAGE, heroAircraft.getLocationX() - ImageManager.HERO_IMAGE.getWidth() / 2,
                heroAircraft.getLocationY() - ImageManager.HERO_IMAGE.getHeight() / 2, null);

        //绘制道具
        paintImageWithPositionRevised(g, propList);

        //绘制得分和生命值
        paintScoreAndLife(g);

    }

    private void paintImageWithPositionRevised(Graphics g, List<? extends AbstractFlyingObject> objects) {
        if (objects.size() == 0) {
            return;
        }

        for (AbstractFlyingObject object : objects) {
            BufferedImage image = object.getImage();
            assert image != null : objects.getClass().getName() + " has no image! ";
            g.drawImage(image, object.getLocationX() - image.getWidth() / 2,
                    object.getLocationY() - image.getHeight() / 2, null);
        }
    }

    private void paintScoreAndLife(Graphics g) {
        int x = 10;
        int y = 25;
        g.setColor(new Color(16711680));
        g.setFont(new Font("SansSerif", Font.BOLD, 22));
        g.drawString("SCORE:" + this.score, x, y);
        y = y + 20;
        g.drawString("LIFE:" + this.heroAircraft.getHp(), x, y);
    }

}
